#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<ctype.h>
#define NUM 1024
#define OPT_NUM 64
#define NONE_REDIR   0
#define INPUT_REDIR  1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3

#define trimspace(start) do{\
   while(isspace(*start)) ++start;\
    }while(0)//遇到空格就跳过


char lineCommand[NUM];
char *myargv[OPT_NUM]; //指针数组
int  lastCode = 0;
int  lastSig = 0;

int redirtype=NONE_REDIR;
char* redirFile=NULL;
//"ls -a -l > myfile.txt" -> "ls -a -l" "myfile.txt"
void commandcheck(char* commands)
{
  assert(commands);//首先传过来的字符串不能为空
  char* start=commands;//字符串首个字母
  char* end=commands+strlen(commands);//字符串后面的斜杠n
  while(start<end)
  {
    if(*start=='>')
    {//"ls -a -l > myfile.txt"
      *start='\0';
      start++;
      if(*start=='>')
      {
    //"ls -a -l >> myfile.txt"
        redirtype=APPEND_REDIR;
        start++;
      }
      trimspace(start);
         redirFile=start;
         break;
    }
    else if(*start=='<')
    {
        //"cat < file.txt"
        *start ='\0';
        start++;
        trimspace(start);
         redirtype=INPUT_REDIR;
         redirFile=start;
    }else {
      start++;
    }
  }

}

int main()
{
    while(1)
    {//子进程每次到这里都要初始化
       redirtype=NONE_REDIR;
       redirFile=NULL;
       errno=0;

        // 输出提示符
        printf("用户名@主机名 当前路径# ");
        fflush(stdout);

        // 获取用户输入, 输入的时候，输入\n
        char *s = fgets(lineCommand, sizeof(lineCommand)-1, stdin);
        assert(s != NULL);
        (void)s;
        // 清除最后一个\n , abcd\n
        lineCommand[strlen(lineCommand)-1] = 0; // ?
        //printf("test : %s\n", lineCommand);
                  
        // "ls -a -l -i" -> "ls" "-a" "-l" "-i" -> 1->n
       commandcheck(lineCommand);//判断重定向把字符串传过去
        // 字符串切割
        myargv[0] = strtok(lineCommand, " ");
        int i = 1;
        if(myargv[0] != NULL && strcmp(myargv[0], "ls") == 0) {
              myargv[i++] = (char*)"--color=auto";
          }
  
          // 如果没有子串了，strtok->NULL, myargv[end] = NULL
        while(myargv[i++] = strtok(NULL, " "));

        // 如果是cd命令，不需要创建子进程,让shell自己执行对应的命令，本质就是执行系统接口
        // 像这种不需要让我们的子进程来执行，而是让shell自己执行的命令 --- 内建/内置命令
        if(myargv[0] != NULL && strcmp(myargv[0], "cd") == 0)
        {
            if(myargv[1] != NULL) chdir(myargv[1]);
            continue;
       }
        if(myargv[0] != NULL && myargv[1] != NULL && strcmp(myargv[0], "echo") == 0)
        {
            if(strcmp(myargv[1], "$?") == 0)
            {
                printf("%d, %d\n", lastCode, lastSig);
            }
            else
            {
                printf("%s\n", myargv[1]);
            }
            continue;
        }
        // 测试是否成功, 条件编译
#ifdef DEBUG
        for(int i = 0 ; myargv[i]; i++)
        {
            printf("myargv[%d]: %s\n", i, myargv[i]);
        }
#endif
        // 内建命令 --> echo

        // 执行命令
        pid_t id = fork();
        assert(id != -1);

        if(id == 0)//子进程
        {
          switch(redirtype)
          {
            case NONE_REDIR:
              //do nothing
              break;
            case INPUT_REDIR:
              {
                  int fd=open(redirFile,O_RDONLY);
                  if(fd<0)
                  {
                    perror("open fail");
                    exit(errno);
                  }
                  dup2(fd,0);//
              }
                break;
            case OUTPUT_REDIR:
            case APPEND_REDIR:
                {
                  umask(0);
                  int flags=O_CREAT|O_WRONLY;
                  if(redirtype==APPEND_REDIR)flags|=O_APPEND;
                   else flags |=O_TRUNC;
                   int fd=open(redirFile,flags,0666);
                   if(fd<0)
                   {
                     perror("open");
                     exit(2);
                   }
                   dup2(fd,1);
                }
                break;
            default:
                printf("this is the bug???\n");
          }
            execvp(myargv[0], myargv);
            exit(1);
        }
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        assert(ret > 0);
        (void)ret;
        lastCode = ((status>>8) & 0xFF);
        lastSig = (status & 0x7F);
  }
}


//#include<stdio.h>
//#include<unistd.h>
//#include<string.h>
//#include<stdlib.h>
//#include<sys/types.h>
//#include<sys/wait.h>
//#include<assert.h>
//
//#define NUM 1024
//#define OPT_NUM 64
//
//char lineCommand[NUM];
//char *myargv[OPT_NUM];
//int main()
//{
////while(1)
////{
//  printf("用户名@主机名 当前路径# ");
//    fflush(stdout);//刷新缓冲区
//char *s=fgets(lineCommand,sizeof(lineCommand)-1,stdin);//把输入的字符串存入lineCommand字符串里，-1是至少预留一个位置给/0
//assert(s!=NULL);//不能输入空
//(void)s;//不需要了就转void
//lineCommand[strlen(lineCommand-1)]=0;//把字符串后面的\n顶掉
//
////字符串切割
//myargv[0]=strtok(lineCommand," ");//以第二个参数作为切割线，切割第一个参数的字符串
//int i=1;
//while(myargv[i++]=strtok(NULL," "));
//
//#ifdef DEBUG//条件编译 
//for(int i=0;myargv[i];i++)
//{
//  printf("myargv[%d]:%s\n",i,myargv[i]);
//
//}
//#endif
//
////执行命令
////pid_t id=fork();
////if(id==0)
////{
////  //子进程执行
////  execvp(myargv[0],myargv);
////  exit(1);
////}
////waitpid(id,NULL,0);
////
////}
//
//}
